// Copyright © SixtyFPS GmbH <info@slint.dev>
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0

import { ScrollView, LineEdit } from "std-widgets-impl.slint";
import { StateLayer, IconButton, IconButtonStyle, SelectionButton, SelectionButtonStyle, ColoredTextStyle } from "./internal-components.slint";

export struct Date {
    year: int,
    month: int,
    day: int,
}

export struct CalendarDelegateStyle {
    font-size: length,
    font-weight: float,
    foreground: brush,
    state-brush: brush,
    background-selected: brush,
    foreground-selected: brush,
    state-brush-selected: brush,
    border-color-today: brush,
    foreground-today: brush,
    state-brush-today: brush,
}

component CalendarHeaderDelegate {
    in property <string> text <=> text-label.text;
    in property <length> font-size;
    in property <float> font-weight;
    in property <brush> foreground;

    min-width: max(40px, content-layer.min-width);
    min-height: max(40px, content-layer.min-height);

    content-layer := VerticalLayout {
        text-label := Text {
            vertical-alignment: center;
            horizontal-alignment: center;
            font-size: root.font-size;
            font-weight: root.font-weight;
            color: root.foreground;
        }
    }
}

component CalendarDelegate {
    in property <bool> selected;
    in property <bool> today;
    in property <string> text <=> text-label.text;
    in property <CalendarDelegateStyle> style;
    in property <bool> enabled: true;

    callback clicked <=> touch-area.clicked;

    min-width: max(40px, content-layer.min-width);
    min-height: max(40px, content-layer.min-height);
    forward-focus: focus-scope;

    accessible-role: button;
    accessible-checkable: true;
    accessible-checked: root.selected;
    accessible-label: root.text;
    accessible-action-default => { touch-area.clicked(); }

    touch-area := TouchArea {
        enabled: root.enabled;
    }

    focus-scope := FocusScope {
        width: 0px;
        enabled: root.enabled;

        key-pressed(event) => {
            if (event.text == " " || event.text == "\n") {
                touch-area.clicked();
                 return accept;
            }

            return reject;
        }
    }

    background-layer := Rectangle {
        border-radius: self.height / 2;
    }

    content-layer := HorizontalLayout {
        text-label := Text {
            vertical-alignment: center;
            horizontal-alignment: center;
            color: root.style.foreground;
            font-size: root.style.font-size;
            font-weight: root.style.font-weight;
        }
    }

    state-layer := StateLayer {
        enabled: root.enabled;
        border-radius: background-layer.border-radius;
        pressed: touch-area.pressed;
        has-hover: touch-area.has-hover;
        has-focus: focus-scope.has-focus;
        state-brush: root.style.state-brush;
    }

    states [
        disabled when !root.enabled : {
            root.opacity: 0.38;
        }
        selected when root.selected : {
            background-layer.background: root.style.background-selected;
            text-label.color: root.style.foreground-selected;
            state-layer.state-brush: root.style.state-brush-selected;
        }
        today when root.today : {
            background-layer.border-color: root.style.border-color-today;
            background-layer.border-width: 1px;
            state-layer.state-brush: root.style.state-brush-today;
        }
    ]
}

export struct CalendarStyle {
    delegate-style: CalendarDelegateStyle,
    spacing: length,
}

export component Calendar {
    in property <int> column-count;
    in property <int> row-count;
    in property <length> delegate-size;
    in property <CalendarStyle> style;
    in property <int> start-column;
    in property <[string]> header-model;
    in property <int> month-count;
    in property <Date> today;
    in property <Date> selected-date;
    in property <int> display-month;
    in property <int> display-year;

    callback select-date(date: Date);

    // header
    for day[index] in root.header-model : CalendarHeaderDelegate {
        x: root.delegate-x(index);
        y: root.delegate-y(index);
        text: day;
        font-size: root.style.delegate-style.font-size;
        font-weight: root.style.delegate-style.font-weight;
        foreground: root.style.delegate-style.foreground;
    }

    // items
    for index in root.month-count : CalendarDelegate {
        property <Date> d: { day: index + 1, month: root.display-month, year: root.display-year };

        x: root.delegate-x(root.index-on-calendar(index));
        y: root.delegate-y(root.index-on-calendar(index));
        width: root.delegate-size;
        height: root.delegate-size;
        text: index + 1;
        style: root.style.delegate-style;
        selected: root.selected-date == self.d;
        today: root.today == self.d;

        clicked => {
            root.select-date(self.d);
        }
    }

    function index-on-calendar(index: int) -> int {
        // add column count because items starts after header row
        root.column-count + root.start-column + index
    }

    function row-for-index(index: int) -> int {
        floor(index / root.column-count)
    }

    function column-for-index(index: int) -> int {
        mod(index, root.column-count)
    }

    function delegate-x(index: int) -> length {
        root.column-for-index(index) * root.delegate-size + root.column-for-index(index) * root.style.spacing
    }

    function delegate-y(index: int) -> length {
        root.row-for-index(index) * root.delegate-size + root.row-for-index(index) * root.style.spacing
    }
}

component YearSelection {
    in property <[int]> model;
    in property <length> spacing;
    in property <int> visible-row-count;
    in property <int> column-count;
    in property <length> delegate-width;
    in property <length> delegate-height;
    in property <CalendarDelegateStyle> delegate-style;
    in property <int> selected-year;
    in property <int> today-year;

    callback select-year(year: int);

    property <length> row-height: root.height / root.visible-row-count;
    property <int> row-count: root.model.length / root.column-count;
    property <length> viewport-height: root.row-count * root.row-height;
    property <length> start-x: root.width / (root.column-count + 1);
    property <length> start-y: root.height / (root.visible-row-count + 1);

    ScrollView {
        width: 100%;
        height: 100%;
        viewport-width: root.width;
        viewport-height: root.viewport-height;

        for year[index] in root.model: CalendarDelegate {
            x: root.delegate-center-x(index) - self.width / 2;
            y: root.delegate-center-y(index) - self.height / 2;
            width: root.delegate-width;
            height: root.delegate-height;
            text: year;
            style: root.delegate-style;
            selected: year == root.selected-year;
            today: year == root.today-year;

            clicked => {
                root.select-year(year);
            }
        }
    }

    function delegate-center-x(index: int) -> length {
        root.start-x * (root.column-for-index(index) + 1)
    }

    function delegate-center-y(index: int) -> length {
        root.start-y * (root.row-for-index(index) + 1)
    }

    function row-for-index(index: int) -> int {
        floor(index / root.column-count)
    }

    function column-for-index(index: int) -> int {
        mod(index, root.column-count)
    }
}

export struct DatePickerStyle {
    border-brush: brush,
    horizontal-spacing: length,
    vertical-spacing: length,
    calendar-style: CalendarStyle,
    icon-button-style: IconButtonStyle,
    selection-button-style: SelectionButtonStyle,
    current-day-style: ColoredTextStyle,
    title-style: ColoredTextStyle,
    next-icon: image,
    previous-icon: image,
    drop-down-icon: image,
    input-icon: image,
    calendar-icon: image,
}

export component DatePickerBase {
    in property <Date> date : { day: today[0], month: today[1], year: today[2] };
    in property <DatePickerStyle> style;
    in property <string> title;
    in property <string> input-title: @tr("Enter date");
    in property <string> input-placeholder-text: "mm/dd/yyyy";
    in property <string> input-format: "%m/%d/%Y";

    // this is used for the navigation between months
    property <Date> display-date: root.date;
    property <Date> current-date: root.date;
    property <length> delegate-size: 40px;
    property <length> year-delegate-width: 72px;
    property <int> calendar-column-count: 7;
    property <int> calendar-row-count: 6;
    property <length> calendar-min-width: root.delegate-size * root.calendar-column-count + (root.calendar-column-count - 1) * root.style.calendar-style.spacing;
    property <length> calendar-min-height: root.delegate-size *(root.calendar-row-count + 1) + (root.calendar-row-count - 1) * root.style.calendar-style.spacing;
    property <int> year-selection-column-count: 3;
    property <int> year-selection-row-count: 5;
    property <bool> year-selection;
    property <bool> selection-mode: true;
    property <string> current-input;

    property <int> calendar-month-count: SlintInternal.month_day_count(root.display-date.month, root.display-date.year);
    property <[string]> calendar-header-model: [
        @tr("One-letter abbrev for Sunday" => "S"),
        @tr("One-letter abbrev for Monday" => "M"),
        @tr("One-letter abbrev for Tuesday" => "T"),
        @tr("One-letter abbrev for Wednesday" => "W"),
        @tr("One-letter abbrev for Thursday" => "T"),
        @tr("One-letter abbrev for Friday" => "F"),
        @tr("One-letter abbrev for Saturday" => "S"),
    ];
    property <[int]> today: SlintInternal.date_now();
    property <int> start-column: SlintInternal.month_offset(root.display-date.month, root.display-date.year);
    property <string> current-month: SlintInternal.format_date("%B %Y", root.display-date.day, root.display-date.month, root.display-date.year);
    property <[int]> year-model: [2024, 2025, 2026, 2027, 2028, 2029, 2031, 2032, 2033, 2034, 2035, 2036, 2037, 2038, 2039, 2040, 2041, 2042, 2043];
    property <int> selected-year: root.display-date.year;
    property <int> today-year: root.today[2];
    property <string> current-day: SlintInternal.format_date("%a, %b %d", root.current-date.day, root.current-date.month, root.current-date.year);
    property <[int]> input-formatted: SlintInternal.parse_date(root.current-input, root.input-format);

    min-width: content-layer.min-width;
    min-height: content-layer.min-height;

    content-layer := VerticalLayout {
        spacing: root.style.vertical-spacing;

        Text {
            text: root.title;
            horizontal-alignment: left;
            overflow: elide;
            font-size: root.style.title-style.font-size;
            font-weight: root.style.title-style.font-weight;
            color: root.style.title-style.foreground;
        }

        header := HorizontalLayout {
            spacing: root.style.horizontal-spacing;

            Text {
                text: root.selection-mode ? root.current-day : root.input-title;
                horizontal-alignment: left;
                vertical-alignment: center;
                font-size: root.style.current-day-style.font-size;
                font-weight: root.style.current-day-style.font-weight;
                color: root.style.current-day-style.foreground;
            }

            IconButton {
                icon: root.selection-mode ? root.style.input-icon : root.style.calendar-icon;
                style: root.style.icon-button-style;
                accessible-label: "Toggle selection mode";

                clicked => {
                    root.toggle-selection-mode();
                }
            }
        }

        Rectangle {
            height: 1px;
            background: root.style.border-brush;
        }

        if root.selection-mode : HorizontalLayout {
            spacing: root.style.horizontal-spacing;

            VerticalLayout {
                horizontal-stretch: 0;
                alignment: center;

                SelectionButton {
                    text: root.current-month;
                    style: root.style.selection-button-style;
                    icon: root.style.drop-down-icon;
                    checked <=> root.year-selection;
                }
            }

            Rectangle {}

            IconButton {
                icon: root.style.previous-icon;
                style: root.style.icon-button-style;
                accessible-label: "Previous month";

                clicked => {
                    root.show-previous();
                }
            }

            IconButton {
                icon: root.style.next-icon;
                style: root.style.icon-button-style;
                accessible-label: "Next month";

                clicked => {
                    root.show-next();
                }
            }
        }

        if root.selection-mode : VerticalLayout {
            spacing: root.style.vertical-spacing;

            if !root.year-selection : Calendar {
                min-width: root.calendar-min-width;
                min-height: root.calendar-min-height;
                column-count: root.calendar-column-count;
                row-count: root.calendar-row-count;
                delegate-size: root.delegate-size;
                style: root.style.calendar-style;
                header-model: root.calendar-header-model;
                month-count: root.calendar-month-count;
                today: { day: root.today[0], month: root.today[1], year: root.today[2] };
                selected-date <=> root.current-date;
                start-column: root.start-column;
                display-month: root.display-date.month;
                display-year: root.display-date.year;

                select-date(date) => {
                    root.select-date(date);
                }
            }

            if root.year-selection : YearSelection {
                min-width: root.calendar-min-width;
                min-height: root.calendar-min-height;
                spacing: root.style.calendar-style.spacing;
                column-count: root.year-selection-column-count;
                visible-row-count: root.year-selection-row-count;
                delegate-width: root.year-delegate-width;
                delegate-height: root.delegate-size;
                model: root.year-model;
                delegate-style: root.style.calendar-style.delegate-style;
                selected-year: root.selected-year;
                today-year: root.today-year;

                select-year(year) => {
                    root.select-year(year);
                }
            }
        }

        Rectangle {
            height: 1px;
            background: root.style.border-brush;
            visible: root.year-selection;
        }

        if !root.selection-mode : LineEdit {
            text <=> root.current-input;
            placeholder-text: root.input-placeholder-text;
        }
    }

    changed date => {
        root.display-date = root.date;
        root.current-date = root.date;
    }

    changed selection-mode => {
        // check switch from input mode if input is valid
        if root.selection-mode && root.current-input-valid() {
            root.current-date = root.input-as-date();
            root.display-date = root.current-date;
        }
    }

    pure public function ok-enabled() -> bool {
        root.selection-mode || root.current-input-valid()
    }

    public function get-current-date() -> Date {
        if root.selection-mode {
            return root.current-date;
        }

        root.input-as-date()
    }

    pure function current-input-valid() -> bool {
        SlintInternal.valid_date(root.current-input, root.input-format)
    }

    pure function input-as-date() -> Date {
        { day: root.input-formatted[0], month: root.input-formatted[1], year: root.input-formatted[2] }
    }

    function select-date(date: Date) {
        root.current-date = date;
    }

    function select-year(year: int) {
        root.current-date = { day: 1, month: 1, year: year };
        root.display-date = root.current-date;
        root.year-selection = false;
    }

    function show-next() {
        if root.display-date.month >= 12 {
            root.display-date = { day: 1, month: 1, year: root.display-date.year + 1 };
            return;
        }

        root.display-date = { day: 1, month: root.display-date.month + 1, year: root.display-date.year };
    }

    function show-previous() {
        if root.display-date.month <= 1 {
            root.display-date = { day: 1, month: 12, year: root.display-date.year - 1 };
            return;
        }

        root.display-date = { day: 1, month: root.display-date.month - 1, year: root.display-date.year };
    }

    function toggle-selection-mode() {
        root.selection-mode = !root.selection-mode;
    }
}
